---
title: contentlayer-next-docs-page-with-nav
sortNum: 102
---
### contentlayer-next-docs-page-with-nav
Nav between documents and on page heading elements
#### Pasted files structure
```bash
├── app
│   └── docs
│       └── [[...slug]]
│           ├── layout.tsx
│           └── page.tsx
└── components
    └── DocsSideNav.tsx
```
#### Files contents
```tsx title="./app/docs/[[...slug]]/layout.tsx"
import DocsSideNav from "@/components/DocsSideNav";
import PageContentNav from "@/components/PageContentNav";
import '../../../styles/docs.css'

export default function DocsLayout({ children }: { children: React.ReactNode }) {
    return (
        <div className="grid grid-cols-[1fr_3fr_1fr] max-w-screen-xl mx-auto mt-10 relative mb-32">
            <DocsSideNav />
            {children}
            <PageContentNav />
        </div>
    )
}
```
```tsx title="./app/docs/[[...slug]]/page.tsx"
import { allDocs } from "@/.contentlayer/generated"
import { Mdx } from "@/components/mdx-components"
import { redirect } from 'next/navigation'
import { MdKeyboardArrowRight } from 'react-icons/md'

const getDocBySlugs = (slugs: string[]) => {
    const pathFromSlugs = slugs.join('/')
    const doc = allDocs.find((doc) => doc._raw.flattenedPath === pathFromSlugs)
    if (!doc) return redirect('/docs/Basic use/Save and Paste')
    return doc
}

export default function DocsPage({ params }: { params: { slug?: string[] } }) {

    if (!params.slug) return redirect('/docs/Basic use/Save and Paste')
    params.slug = params.slug?.map((slug) => decodeURI(slug))

    const doc = getDocBySlugs(params.slug)

    return (
        <article className="min-h-screen pb-8 overflow-x-auto px-8 border-l">
            <div className="flex mb-4 gap-1 items-center text-sm">
                <p className="text-muted-foreground">{doc._raw.sourceFileDir}</p>
                <MdKeyboardArrowRight className="text-muted-foreground" />
                <p className="font-medium text-indigo-800 dark:bg-zinc-900 dark:text-indigo-600 bg-indigo-100 px-2 rounded-full">{doc.title}</p>
            </div>
            <Mdx code={doc?.body.code!} />
        </article>
    )
}
```
```tsx title="./components/DocsSideNav.tsx"
'use client'

import { usePathname } from 'next/navigation'
import { Doc, allDocs } from "@/.contentlayer/generated";
import Link from "next/link";
import { cn } from '@/lib/utils';


export default function DocsSideNav() {
    const sortedArr = allDocs.sort((a, b) => a.sortNum - b.sortNum)
    const path = decodeURI(usePathname())

    const categorizedDocs = sortedArr.reduce((acc: { [key: Doc['_raw']['sourceFileDir']]: Doc[] }, doc) => {
        const currCatContent = acc[doc._raw.sourceFileDir]
        return {
            ...acc,
            [doc._raw.sourceFileDir]: [...(currCatContent ? currCatContent : []), doc]
        }
    }, {})

    return (
        <div className="relative pr-6">
            <div className='sticky top-[7.04rem] text-sm'>
                {Object.entries(categorizedDocs).map(([cat, docs], i) => {
                    return (
                        <div key={i} className="mb-4">
                            <p className="text-foreground font-semibold mb-2">{cat}</p>
                            <div className="flex flex-col gap-1">
                                {docs.map((doc, j) => {
                                    const isActive = path === `/docs/${doc._raw.flattenedPath}`

                                    return <Link key={j} style={{ contain: "inline-size" }} href={`/docs/${doc._raw.flattenedPath}`} className={cn("ml-0 text-muted-foreground hover:text-indigo-700 dark:hover:text-indigo-600 transition-all hover:bg-indigo-50 py-1 px-2 rounded-md text-ellipsis whitespace-nowrap overflow-hidden dark:hover:bg-zinc-900", isActive ? "bg-indigo-50 dark:bg-zinc-900 dark:text-indigo-600 text-indigo-700" : "")}>{doc.title}</Link>
                                })}
                            </div>
                        </div>
                    )
                })}
            </div>
        </div>
    )
}
```
